#include <stdlib.h>
#include "Arbre.h"
#include "Symbole.h"
#include "SymboleValue.h"
#include "typeinfo"

////////////////////////////////////////////////////////////////////////////////
// NoeudSeqInst
////////////////////////////////////////////////////////////////////////////////

NoeudSeqInst::NoeudSeqInst() : tabInst() {
}

////////////////////////////////////////////////////////////////////////////////
int NoeudSeqInst::getValeur() {
  int valeur=0;
  for (unsigned int i=0; i<tabInst.size(); i++)
    valeur = tabInst[i]->getValeur();  // on evalue chaque instruction de la séquence
  return valeur; // par convention, resultat = valeur de la derniere instruction
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Sequence de " << tabInst.size() << " instruction(s)" << endl;
  for (unsigned int i=0; i<tabInst.size(); i++)
    tabInst[i]->afficher(indentation+1); // on affiche les fils en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
void NoeudSeqInst::ajouteInstruction(Noeud* instruction) {
  tabInst.push_back(instruction);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudAffectation
////////////////////////////////////////////////////////////////////////////////

NoeudAffectation::NoeudAffectation(Noeud* variable, Noeud* expression) {
  this->variable=variable;
  this->expression=expression;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudAffectation::getValeur() {
  int valeur = expression->getValeur(); // on évalue l'expression
  ((SymboleValue*)variable)->setValeur(valeur); // on affecte la variable
  return valeur; // par convention, une affectation a pour valeur la valeur affectée
}

////////////////////////////////////////////////////////////////////////////////
void NoeudAffectation::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Affectation" << endl;
  variable->afficher(indentation+1);   // on affiche variable et expression
  expression->afficher(indentation+1); // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurAdd
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurAdd::NoeudOperateurAdd ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}

////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurAdd::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if      (this->operateur=="+") valeur = og+od; 
  else if (this->operateur=="-") valeur = og-od;
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurAdd::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Add \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurMult
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurMult::NoeudOperateurMult ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurMult::getValeur() { 
  int valeur=0;
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  if  (this->operateur=="*") valeur = og*od; 
  else if (this->operateur=="/"){
      try{
	if (od!=0)
		valeur = og/od;
  	else {
	  	cout << "Erreur pendant l'interpretation : division par zero" << endl;
	  	exit(0); // plus tard on levera une exception
        }
      } catch (int)
      {
          throw;
      }
  	}
  return valeur;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurMult::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Mult \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurEt
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurEt::NoeudOperateurEt ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurEt::getValeur() { 
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();

  return (og && od);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurEt::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Et \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurOu
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurOu::NoeudOperateurOu ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurOu::getValeur() { 
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();

  return (og || od);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurOu::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Ou \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurRel
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurRel::NoeudOperateurRel ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurRel::getValeur() { 
  int og=operandeGauche->getValeur();
  int od=operandeDroit->getValeur();
  
   if  (this->operateur=="==") return (og==od); 
   else if (this->operateur=="!=") return (og!=od);
   else if (this->operateur==">=") return (og>=od);
   else if (this->operateur=="<=") return (og<=od);
   else if (this->operateur==">") return (og>od);
   else if (this->operateur=="<") return (og<od);
   else return 0;
}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurRel::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Rel \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudOperateurUnaire
////////////////////////////////////////////////////////////////////////////////

NoeudOperateurUnaire::NoeudOperateurUnaire ( Symbole operateur,
                                               Noeud* operandeGauche,
                                               Noeud* operandeDroit) {
  this->operateur=operateur;
  this->operandeGauche=operandeGauche;
  this->operandeDroit=operandeDroit;
}
////////////////////////////////////////////////////////////////////////////////
int NoeudOperateurUnaire::getValeur() { 

  int od=operandeDroit->getValeur();
  
   if  (this->operateur=="-") return (0-od); 
   else if (this->operateur=="non") return (!od);
   else return 0;

}

////////////////////////////////////////////////////////////////////////////////
void NoeudOperateurUnaire::afficher(unsigned short indentation) {
  Noeud::afficher(indentation);
  cout << "Noeud - Operateur Unaire \"" << this->operateur.getChaine() << "\" applique a : " << endl;
  operandeGauche->afficher(indentation+1);  // on affiche fils gauche et fils droit
  operandeDroit->afficher(indentation+1);   // en augmentant l'indentation
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSi
////////////////////////////////////////////////////////////////////////////////

NoeudInstSi::NoeudInstSi() : VectCond() , VectSeqInst(){}

void NoeudInstSi::ajoute(Noeud* cond, Noeud* seq){
    VectCond.push_back(cond);
    VectSeqInst.push_back (seq);
}

int NoeudInstSi::getValeur() {
    unsigned int i=0;
    while (i<VectCond.size() && VectCond[i]!=NULL && !VectCond[i]->getValeur()){
        i=i+1;
    }
    if(i<VectCond.size()){
        VectSeqInst[i]->getValeur();
    }
    return 0;
}

void NoeudInstSi::afficher(unsigned short indentation) {
     unsigned int i = 0;
    Noeud::afficher(indentation);
    cout << "Noeud - InstSi " << endl;
    while (i<VectCond.size()){
        if (i==0){
        cout << "Si :" << endl;
        VectCond[i]->afficher(indentation+1);
        VectSeqInst[i]->afficher(indentation+1);
        }
        else if (i>0 && VectCond[i] != NULL) {
        cout << "Sinon si :" << endl;
        VectCond[i]->afficher(indentation+1);
        VectSeqInst[i]->afficher(indentation+1);
        }
        else {
        cout << "Sinon :" << endl;
        VectSeqInst[i]->afficher(indentation+1);
        }
        i++;
    }
}
////////////////////////////////////////////////////////////////////////////////
// NoeudInstTq
////////////////////////////////////////////////////////////////////////////////

NoeudInstTq::NoeudInstTq(Noeud* cond , Noeud* seq) {
    this->cond=cond;
    this->seq=seq;
}

int NoeudInstTq::getValeur() {
    while (cond->getValeur()){
        seq->getValeur();
    }
    return 0;
}

void NoeudInstTq::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - InstTq " << endl;
    cout << "Tant que :" << endl;
    cond->afficher(indentation+1);
    cout << "Faire : " << endl;
    seq->afficher(indentation+1);
    
}

////////////////////////////////////////////////////////////////////////////////
Noeud* NoeudInstTq::getcond(){
    return(this->cond);
}
    
////////////////////////////////////////////////////////////////////////////////
Noeud* NoeudInstTq::getseq(){
    return(this->seq);

}
    
////////////////////////////////////////////////////////////////////////////////
// NoeudInstPour
////////////////////////////////////////////////////////////////////////////////

NoeudInstPour::NoeudInstPour(Noeud* affect1, Noeud* cond,Noeud* affect2, Noeud* seq) {
    this->affect1=affect1;
    this->cond=cond;
    this->affect2=affect2;
    this->seq=seq;
}

int NoeudInstPour::getValeur() {
    for (this->affect1->getValeur() ; this->cond->getValeur() ; this->affect2->getValeur())
    {
        this->seq->getValeur();
    }
    return 0;
}

void NoeudInstPour::afficher(unsigned short indentation) {

    Noeud::afficher(indentation);
    cout << "Noeud - InstPour " << endl;
    cout << "Pour :" << endl;
    affect1->afficher(indentation+1);
    cout << " ; ";
    cond->afficher(indentation+1);
    cout << " ; " ;
    affect2->afficher(indentation+1);
    cout << endl;
    seq->afficher(indentation+1);
    
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstRepeter
////////////////////////////////////////////////////////////////////////////////


NoeudInstRepeter::NoeudInstRepeter(Noeud* cond, Noeud* seq):NoeudInstTq(cond,seq) {}


////////////////////////////////////////////////////////////////////////////////
int NoeudInstRepeter::getValeur() {
    Noeud* cond = this->getcond();
    Noeud* seq = this->getseq();
    
    seq->getValeur();
    while(!cond->getValeur()){
        seq->getValeur();
  }      
  return 0;                                  
}


////////////////////////////////////////////////////////////////////////////////
void NoeudInstRepeter::afficher(unsigned short indentation)
{
    Noeud* cond = this->getcond();
    Noeud* seq = this->getseq();
    Noeud::afficher(indentation);
    cout << "Noeud - Repeter : " << endl;
    cout << "Repeter : " << endl;
    seq->afficher(indentation+1);
    cout << "Jusqua : " << endl;
    cond->afficher(indentation+1);
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstLire
////////////////////////////////////////////////////////////////////////////////
NoeudInstLire::NoeudInstLire(Noeud* variable) {
        this->variable=variable;
}


////////////////////////////////////////////////////////////////////////////////
int NoeudInstLire::getValeur() {
        int var;
        cin >> var;                                // on évalue la variable
        ((SymboleValue*)variable)->setValeur(var); // on affecte la variable
        return 0;                   
}


////////////////////////////////////////////////////////////////////////////////
void NoeudInstLire::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Lire : " << endl;
        variable->afficher(indentation+1);
}



////////////////////////////////////////////////////////////////////////////////
// NoeudInstEcrire
////////////////////////////////////////////////////////////////////////////////


NoeudInstEcrire::NoeudInstEcrire(Noeud* expression) {
    this->expression=expression;
}


////////////////////////////////////////////////////////////////////////////////
int NoeudInstEcrire::getValeur() {
    if (typeid (* expression) == typeid (SymboleValue) && (*(SymboleValue *)expression=="<CHAINE>"))
            cout << ((SymboleValue *)expression)->getChaine().substr(1,((SymboleValue *)expression)->getChaine().size()-2);
    else
            cout << expression->getValeur();
    return 0;
}


////////////////////////////////////////////////////////////////////////////////
void NoeudInstEcrire::afficher(unsigned short indentation)
{
        Noeud::afficher(indentation);
        cout << "Noeud - Ecrire : " << endl;
        expression->afficher(indentation+1);   
}

////////////////////////////////////////////////////////////////////////////////
// NoeudInstSwitch
////////////////////////////////////////////////////////////////////////////////

NoeudInstSwitch::NoeudInstSwitch(Noeud* var) :
        NoeudInstSi(){
                this->variable = var;
}



////////////////////////////////////////////////////////////////////////////////
void NoeudInstSwitch::afficher(unsigned short indentation) {
        Noeud::afficher(indentation);
        unsigned int i = 0;
        cout << "Noeud - InstSwitch \" Switch"
                        << "\" applique a : " << endl;
        cout << variable->getValeur();
        vector<Noeud *> cond;
        vector<Noeud *> seq;
        
        for (i = 0; i<cond.size(); i++) {
                cond[i]->afficher(indentation + 1);
                seq[i]->afficher(indentation + 1);             
        }
        
              
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSwitch::ajoutecond(Noeud* cond){
    vCond.push_back(cond);
}

////////////////////////////////////////////////////////////////////////////////
void NoeudInstSwitch::ajouteseq(Noeud* seq){
    vSeqInst.push_back(seq);
}